<?php
/*=============================================================================
|| ##################################################################
||	phpFoX Konsort
|| ##################################################################
||
||	Copyright		: (C) 2005-2007 PHPFOX Limited a Reality Fox Creation
||	Contact			: info@phpfox.com
||
||	- phpFoX and all of its source code and files are protected by Copyright Laws.
||
||	- The license for phpFoX permits you to install this software on a single domain (.com, .org, .net, etc.).
||
||	- You may also not remove this copyright screen which shows the copyright information and credits for phpFoX (RealityFOX Creations).
||
||	- phpFoX Konsort is NOT a FREE software - http://www.phpfox.com/license/
||
|| ##################################################################
=============================================================================*/

/* $File: ./include/classes/DbItem.class.php, $Author:  $ */

/**
 * Base class for all classes working with db
 * @package core
 */

/**  DbItem -- parent for all classes thats works with entities in db.
 */
class DbItem extends Object
{
    /** DB driver
     * @var Database
     */
    var $oDb;

    /** Table columns (alias=>real_name).
     * @var array
     */
    var $aFields  = array();

    /** Insert/update/load  data (alias=>value).
     * @var array
     */
    var $aData    = array();

    /** Table name
     * @var string
     */
    var $sTable   = '';

    /** Table alias
     * @var string
     */
    var $sAlias = '';

    /** Primary key name
     * @var string
     */
    var $sId      = '';

    /** "Name" field - used for getHash()
     * @var unknown_type
     */
    var $sNameField = '';

    /** Default Constructor.
     */
    function DbItem()
    {
        parent::Object();
        $this->oDb = &Database::get();
    }

    /** Initialises table
     * @param $sTableCode table logical name
     */
    function _initTable($sTableCode, $bLoadFiles = true)
    {
        list($tempTable, $this->sAlias, $this->sId) = App::getTableInfo($sTableCode);

        $this->sTable = App::getParam('db','prefix') . $tempTable;

		if ( $bLoadFiles )
		{
        	$this->_loadFields();
        }
    }

    /** Load table fields
     */
    function _loadFields()
    {
        static $aFieldsCache = array();
        if (isset($aFieldsCache[$this->sTable]))
        {
            //get fields definitions from cache
            $this->aFields = $aFieldsCache[$this->sTable];
        }
        else
        {
            //load field defintions
            $this->aFields = array();
            $sSql = 'SHOW COLUMNS FROM '.$this->sTable;
            $aRows = $this->oDb->getRows($sSql);
            foreach($aRows as $aRow)
            {
                $this->aFields[$aRow['Field']] = $this->sAlias.'.'.$aRow['Field'];
            }
            $aFieldsCache[$this->sTable] = $this->aFields;
        }
    }

    /** Removes from $this->aData incorrect keys (used $this->aFields by default)
     * @param $aField field for filtering, if omitted used $this->aFields
     * @return true
     */
    function _filterData($aData, $aFields = array())
    {
        if (! $aFields)
            $aFields = array_keys($this->aFields);
        foreach($this->aData as $sKey=>$sVal)
        {
            if (! in_array($sKey, $aFields))
                unset($aData[$sKey]);
        }
        return $aData;
    }

    /** Set data to $this->aData
     * @param array  $aData  new data
     */
    function setData($aData)
    {
        assert(is_array($aData));
        foreach($aData as $k => $v)
        {
            $this->aData[$k] = $v;
        }
    }

    /** Get item ID
     * @return int item ID or null if no ID
     */
    function getId()
    {
        return isset($this->aData[$this->sId]) ? $this->aData[$this->sId] : null;
    }

    /** Performs insert.
    * @return int new item ID or false on error
    */
    function insert($bEscape=true)
    {
        $this->aData = $this->_filterData($this->aData);
        $iId = $this->oDb->insert($this->sTable, $this->aData, $bEscape);
        if (!$iId)
            return $this->_addError('dbitem.insert');

		$this->aData[$this->sId] = $iId;
        return $iId;
    }

    /** Performs update of current item
     * ( You should fill in $this->aData.)
     * @param array $aFields fields to update: all other fields will be omitted
     *                       or empty array for no filtration
     * @param bool  $bEsacape true (default) - escape all data, false - do not escape
     * @return bool
     */
    function update($aFields=array(), $bEscape=true)
    {

        if (isset($this->aData[$this->sId]))
        {
            $nId = $this->aData[$this->sId];
        }
        else
        {
            return $this->_addError('dbitem.error.no_id', true);
        }

        //trucate fields set (for security purposes)
        $aData = $this->_filterData($this->aData, $aFields);
        unset($aData[$this->sId]);

        if ($this->oDb->update($this->sTable, $aData, $this->sId.' = '.intval($nId), $bEscape))
            return true;
        else
            return $this->_addError('dbitem.error.update');
    }

    /** Removes record by PK
     * @param mixed $nId PK value
     * @return mixed result of oDb->query
     */
    function delete($mId)
    {
        if (!$mId)
        	return true;

        if (is_array($mId))
            $mId = implode(', ', $mId);
        $sSql = 'DELETE FROM `'.$this->sTable.'` WHERE '.$this->sId.' IN('.$mId.')';
        return $this->oDb->query($sSql);
    }

    /** Loads info from DB by PK
     * @param int $nId PK
     * @return boolean 1 - loaded, false - not found
     */
    function load($nId)
    {
        $sSql = 'SELECT '.join(', ', $this->aFields).' FROM `'.$this->sTable.'` AS '.$this->sAlias.' WHERE '.$this->sId.'="'.$nId.'"';
        $this->aData = $this->oDb->getRow($sSql);
        return sizeof($this->aData);
    }

    /**
     * Load info from DB by condition. More than one rows
     * selected then only first loaded.
     *
     * @param string $sCondition
     * @return bool
     */
    function loadByCondition($sCondition)
    {
        $sSql = 'SELECT '.join(', ', $this->aFields).' FROM `'.$this->sTable.'` AS '.$this->sAlias.' WHERE '.$sCondition;
        $this->aData = $this->oDb->getRow($sSql);
        return sizeof($this->aData);
    }


    /** Prepare 'where' statement for sql query
     * @param array $aCond conditions like array('name'=>'LIKE "zz%"', 'price'=>'<12')
     * @param array $aMap mapping like array('name'=>'p.product_name', 'price'=>'p2o.price')
     * @return string where ststement like 1 AND p.product_name LIKE "zz%" AND p2o.price < 12
     */
    function _parseCond($aCond, $aMap)
    {
        $aRes = array('1');
        foreach($aCond as $sKey => $sExp)
        {
            if(is_numeric($sKey))
                $aRes[] = $sExp;
            else
                $aRes[] = $aMap[$sKey].' '.$sExp;
        }

        $sCond = join(' AND ', $aRes);
        foreach($aMap as $k=>$v)
        {
            if (false === strpos($sCond, '{#'))
                break; //no more {#..} -- exit
            $sCond = str_replace('{#'.$k.'}', $v, $sCond);
        }
        return $sCond;
    }

    /**
     * Join fields and add alias
     *
     * @param array $aMap    field mapping
     * @param array $aFields fields to map (if none given then used all mapping keys)
     * @return string joined and aliased fields
     */
    function _joinFields($aMap, $aFields=array())
    {
        $aRes = array();
        $aFields = $aFields ? $aFields : array_keys($aMap);
        foreach($aFields as $sAlias)
        {
            $aRes[] = $aMap[$sAlias].' AS `'.$sAlias.'`';
        }
        return join(', ', $aRes);
    }

    /**
     * Get offset for given page (fix page number if needed)
     *
     * @param int $iPage      page number
     * @param int $iPageSize  page size (rows per page)
     * @param int $iCnt       records number
     * @return int offset of LIMIT in SQL
     */
    function _getOffset($iPage, $iPageSize, $iCnt)
    {
        if ($iPageSize) //if get page -- fix current page and get offset
        {
            $iPages  = ceil($iCnt / $iPageSize);
            $iPage   = max(1, min($iPages, $iPage));
            return $iPageSize*($iPage-1);
        }

        return 0;
    }

    /** Select from DB list of records.
     *
     * @param array  $aCond      conditions like array('name'=>'LIKE "zz%"', 'price'=>'<12') (usually prepared by Filter class)
     * @param int    $iOffset    page number (may be corrected)
     * @param int    $iPageSize  page size (row per page)
     * @param string $sSort      ORDER BY statement (usually prepared by Sorder class)
     * @param string $sKeyField  name of key fields (field that will be user as key for returned array)
     * @return array ($aRows, $iCnt)
     */
    function getList($aCond=array(), $iPage=0, $iPageSize=0, $sSort='', $aFields=array(), $sKeyField='', $sGrab = '', $sLeftJoin = array(), $sGroupBy = '')
    {
        $aMap = $this->aFields;

        $sCond = $this->_parseCond($aCond, $aMap);
        $sSql = 'SELECT COUNT(*) FROM `'.$this->sTable.'` AS '.$this->sAlias.' WHERE '.$sCond .
        	($sGroupBy?(' GROUP BY '.$sGroupBy):'');
        $iCnt = $this->oDb->getField($sSql);
        $aRows = array();
        if ($iCnt)
        {
            $iOffset = $this->_getOffset($iPage, $iPageSize, $iCnt);
            $sSql = 'SELECT '. ($sGrab ? $sGrab : $this->_joinFields($aMap, $aFields)).
                    ' FROM `'.$this->sTable.'` AS '.$this->sAlias.
                    (count($sLeftJoin) ? " LEFT JOIN " . implode(" LEFT JOIN ", $sLeftJoin) : '') .
                    ' WHERE '.$sCond.
                    ($sGroupBy?(' GROUP BY '.$sGroupBy):'').
                    ($sSort?(' ORDER BY '.$sSort):'').
                    ($iPageSize?(' LIMIT '.$iOffset.','.$iPageSize):'');
            $aRows = $this->oDb->getRows($sSql, true, $sKeyField);
        }
        return array($aRows, $iCnt);
    }

    function getListing($sQuery,$iCnt, $iPage=0, $iPageSize=0)
    {
    	$aRows = array();
    	$iOffset = $this->_getOffset($iPage, $iPageSize, $iCnt);
        $sSql = $sQuery.
                ($iPageSize?(' LIMIT '.$iOffset.','.$iPageSize):'');
        $aRows = $this->oDb->getRows($sSql);
        return $aRows;
    }

    /** Select data from table as hash.
     * @param string $sValue name of columns with values
     * @param array  $aCond  condition array
     * @return array hash ($id=>$value)
     */
    function getHash($sValue='name', $aCond=array())
    {
        $sCond = $this->_parseCond($aCond, $this->aFields);
        $sSql = 'SELECT '.$this->sId.','.$sValue.'  FROM `'.$this->sTable.'` AS '. $this->sAlias . ' ' .
                ($sCond?'  WHERE '.$sCond:'').
                '  ORDER BY '.$sValue;
        $aRows = $this->oDb->getRows($sSql);
        $aRes = array();
        foreach($aRows as $aRow)
            $aRes[$aRow[$this->sId]] = $aRow[$sValue];
        return $aRes;
    }
}
?>